import os, re
from CoreEvents import LogEvent
from EventManagement import EventUser
from TackGameEvents import DataLoadEvent

################################################################################
class DataType():
	"""
	Virtual Base DataType class managed by DataManager
	"""
	############################################################################
	def __init__(self, lFileTypes):
		self._lFileTypes = lFileTypes
		self._sKey = None
	############################################################################
	
	def GetKey(self): return self._sKey
	
	############################################################################
	# Public Methods
	############################################################################
	############################################################################
	def LoadFile(self, sPath, dData):
		"""
		Loads a file in to the object ready for use
		
		param : sPath - Path to file
		param - dData : Extra dictionary of data that will be passed on to
			new DataType object
		"""
		
		# Check file is ok
		assert self.__CheckFile(sPath), "Invalid file type"
		
		# File is ok, try to get data from it
		self._LoadData(sPath, dData)
	############################################################################
		
	############################################################################
	# Private Methods
	############################################################################
	############################################################################					
	def __CheckFile(self, sPath):
		"""
		Checks that the file is suitable to be loaded into the object
		
		param : sPath - Path to file
		
		return : bOk - 1 if file is suitable, None if not
		"""
		# Check file exists
		if not os.path.isfile(sPath): return None
		
		# Grab the file
		rMatch = re.search('\/\w+\.(\w+)$', sPath)
		if not rMatch: return None
		
		# Check extension
		for sExt in self._lFileTypes:
			# Found an acceptable file extension?
			if(rMatch.group(1) == sExt): return 1
				
		# Didn't match any supported file types
		return None
	############################################################################
	
	############################################################################
	# Protected Methods
	############################################################################
	############################################################################
	def _LoadData(self, sPath, dData):
		"""
		VIRTUAL - Loads data from a file in to the object, must assign the 
			object a key (self._sKey)
		
		param - sPath : Path to resource file
		param - dData : Extra dictionary of data
		"""
		pass
	############################################################################
################################################################################

################################################################################
class DataManager(EventUser):
	"""
	Virtual Base Management class for easy management of various
		data types
	"""
	############################################################################
	# Public Methods
	############################################################################
	############################################################################
	def __init__(self, cDataClass, cDataRequestClass):
		"""
		param - cDataClass : Classobj of the datatype that this DataManager will
			Manage
		"""
		self._dData = {}		# All Data objects (key = key, value = object)
		self._cDataType = cDataClass
		
		# Register Callbacks
		self.RegisterEvent(cDataRequestClass, self.DataRequest)
		self.RegisterEvent(DataLoadEvent, self.DataLoad)
	############################################################################
	
	############################################################################
	def AddFile(self, sPath, dData=None):
		"""
		Creates a new Data entry in the DataManager
		
		param - sPath : Path to file
		param - dData : Extra dictionary of data that will be passed on to
			new DataType object
		"""
		self.SendEvent(LogEvent({ 
			'sMessage' : "Adding file: " + sPath, 
			'nLogLevel' : 0,
		}))
		
		rData = self._cDataType()
		
		# If path is relative, add rest of path
		if sPath[0] != '/': sPath = os.getcwd() + '/' + sPath
		
		# Try and load datafile in to DataType object
		try:
			rData.LoadFile(sPath, dData)
			sKey = rData.GetKey()
			assert sKey is not None, "No key has been assigned"
			self._dData[sKey] = rData
		except Exception, e:
			self.SendEvent(LogEvent({ 
				'sMessage' : "Error adding file '" + sPath + "': " + str(e), 
				'nLogLevel' : 2,
			}))
	############################################################################
	
	############################################################################
	def AddDir(self, sPath, dData=None):
		"""
		Attempts to add all the files in the given directory to Data
			Manager, the file name is used as the Data Object's key
		
		param : sPath - path to data files
		param : dData - Extra options to pass to AddFile
		"""
		# If path is relative, add rest of path
		if sPath[0] != '/':
			sPath = os.getcwd() + '/' + sPath
			if sPath[:-1] != '/': sPath += '/'
		for sRoot, lDirs, lFiles in os.walk(sPath): #@UnusedVariable
			for sFile in lFiles:
				self.AddFile(sRoot + sFile, dData)
	############################################################################
	
	############################################################################
	def GetData(self, sKey):
		"""
		Will return the data object for the requested key
		
		param - sKey : Key for data object
		
		return - rData : data object
		"""
		return self._dData.get(sKey)
	############################################################################
	
	############################################################################
	def GetKeyList(self):
		"""
		Will return a list of all known keys
		
		return - lKeys : list of all data keys
		"""
		return self._dData.keys()
	############################################################################
	
	############################################################################
	def RemoveData(self, sKey):
		"""
		Removes the data with the given key from the manager
		
		param : sKey- Key for data object
		"""
		try:
			del self._dData[sKey]
		except:
			return
	############################################################################
	
	############################################################################
	def Clear(self):
		"""
		Clears manager of all data
		"""
		for sKey in self._dData.keys():
			del self._dData[sKey]
	############################################################################
	
	############################################################################
	# Callbacks
	############################################################################
	############################################################################
	def DataLoad(self, oEvent):
		"""
		Virtual method that should be overridden in derived classes to load data
			in to the Manager when the game is created
		"""
		pass
	############################################################################
	
	############################################################################
	def DataRequest(self, oEvent):
		"""
		Very common Data Request callback implementation
		
		param - oEvent : DataRequestEvent
		"""
		oEvent.GetEventData("rCallback")(self.GetData(oEvent.GetEventData("sKey")))
	############################################################################
################################################################################
